#include "stdafx.h"

#include "tokens.h"
#include <lean/functional/algorithm.h>
#include <vector>

namespace funpl
{

namespace parser
{

namespace
{

/// Token name-type-pair.
typedef std::pair<util::const_char_range, token_type::t> token_name_type_pair;
typedef std::vector<token_name_type_pair> token_name_type_vector;

/// Token name-type-pair equality predicate.
struct token_name_type_pair_equality
{
	bool operator ()(const token_name_type_pair &l, const token_name_type_pair &r) { return lean::equal(l.first, r.first); }
	bool operator ()(const token_name_type_pair &l, const util::const_char_range &r) { return lean::equal(l.first, r); }
	bool operator ()(const util::const_char_range &l, const token_name_type_pair &r) { return lean::equal(l, r.first); }
};
/// Token name-type-pair order predicate.
struct token_name_type_pair_order
{
	bool operator ()(const token_name_type_pair &l, const token_name_type_pair &r) { return lean::lexicographical_compare(l.first, r.first); }
	bool operator ()(const token_name_type_pair &l, const util::const_char_range &r) { return lean::lexicographical_compare(l.first, r); }
	bool operator ()(const util::const_char_range &l, const token_name_type_pair &r) { return lean::lexicographical_compare(l, r.first); }
};

/// Creates a sorted vector of identifier name-type-pairs.
token_name_type_vector create_identifier_type_map()
{
	token_name_type_vector map;

	typedef token_name_type_pair tntp;
	using namespace token_type;
	using lean::make_char_range;

	const token_name_type_pair raw[] =
	{
		tntp(make_char_range("fun"), kw_fun),
		tntp(make_char_range("def"), kw_def),
		tntp(make_char_range("var"), kw_var),
		tntp(make_char_range("struct"), kw_struct),
	
		tntp(make_char_range("module"), kw_module),
		tntp(make_char_range("import"), kw_import),
	
		tntp(make_char_range("namespace"), kw_namespace),
		tntp(make_char_range("using"), kw_using),
		tntp(make_char_range("typedef"), kw_typedef),
	
		tntp(make_char_range("class") , kw_class),
		tntp(make_char_range("typegroup"), kw_typegroup),
	
		tntp(make_char_range("if"), kw_if),
		tntp(make_char_range("else"), kw_else),
		tntp(make_char_range("for"), kw_for),
		tntp(make_char_range("while"), kw_while),
		tntp(make_char_range("do"), kw_do),

		tntp(make_char_range("return"), kw_return),

		tntp(make_char_range("in"), kw_in),

		tntp(make_char_range("new"), kw_new),
		tntp(make_char_range("delete"), kw_delete),
	
		tntp(make_char_range("throw"), kw_throw),
		tntp(make_char_range("try"), kw_try),
		tntp(make_char_range("catch"), kw_catch),
		tntp(make_char_range("guard"), kw_guard),
		tntp(make_char_range("unguard"), kw_unguard)
	};

	map.assign(raw, raw + lean::arraylen(raw));
	
	std::sort(map.begin(), map.end(), token_name_type_pair_order());

	return map;
}

/// Creates a sorted vector of special 2-character range name-type-pairs.
token_name_type_vector create_special_2_type_map()
{
	token_name_type_vector map;

	typedef token_name_type_pair tntp;
	using namespace token_type;
	using lean::make_char_range;

	const token_name_type_pair raw[] =
	{
		tntp(make_char_range("->"), op_to),
		tntp(make_char_range("=>"), op_on),
		
		tntp(make_char_range("++"), op_increment),
		tntp(make_char_range("--"), op_decrement),

		tntp(make_char_range("&&"), op_logical_and),
		tntp(make_char_range("||"), op_logical_or),
		
		tntp(make_char_range("+="), op_add_assign),
		tntp(make_char_range("-="), op_sub_assign),
		tntp(make_char_range("*="), op_mul_assign),
		tntp(make_char_range("/="), op_div_assign),
		tntp(make_char_range("%="), op_mod_assign),
		
		tntp(make_char_range("&="), op_and_assign),
		tntp(make_char_range("|="), op_or_assign),
		tntp(make_char_range("^="), op_xor_assign),
		
		tntp(make_char_range("=="), op_equal),
		tntp(make_char_range("!="), op_not_equal),
		tntp(make_char_range("<="), op_less_equal),
		tntp(make_char_range(">="), op_greater_equal)
	};

	map.assign(raw, raw + lean::arraylen(raw));
	
	std::sort(map.begin(), map.end(), token_name_type_pair_order());

	return map;
}

/// Creates a sorted vector of special 1-character range name-type-pairs.
token_name_type_vector create_special_1_type_map()
{
	token_name_type_vector map;

	typedef token_name_type_pair tntp;
	using namespace token_type;
	using lean::make_char_range;

	const token_name_type_pair raw[] =
	{
		tntp(make_char_range(";"), tok_semicolon),
		tntp(make_char_range(","), tok_comma),

		tntp(make_char_range(":"), tok_op_colon),
		tntp(make_char_range("."), op_period),

		tntp(make_char_range("?"), tok_questionmark),

		tntp(make_char_range("~"), op_tilde),

		tntp(make_char_range("+"), op_add),
		tntp(make_char_range("-"), op_sub),
		tntp(make_char_range("*"), op_mul),
		tntp(make_char_range("/"), op_div),
		tntp(make_char_range("%"), op_mod),

		tntp(make_char_range("&"), op_and),
		tntp(make_char_range("|"), op_or),
		tntp(make_char_range("^"), op_xor),
		tntp(make_char_range("!"), op_not),
		
		tntp(make_char_range("="), op_assign),
		
		tntp(make_char_range("<"), op_less),
		tntp(make_char_range(">"), op_greater),

		tntp(make_char_range("("), tok_oparen),
		tntp(make_char_range(")"), tok_cparen),

		tntp(make_char_range("{"), tok_obrace),
		tntp(make_char_range("}"), tok_cbrace),

		tntp(make_char_range("["), tok_obracket),
		tntp(make_char_range("]"), tok_cbracket)
	};

	map.assign(raw, raw + lean::arraylen(raw));
	
	std::sort(map.begin(), map.end(), token_name_type_pair_order());

	return map;
}

} // namespace

// Gets the type of the given identifier.
token_type::t identifier_token_type(const util::const_char_range &identifier)
{
	static const token_name_type_vector identifierMap = create_identifier_type_map();

	auto it = lean::find_sorted(
			identifierMap.begin(), identifierMap.end(), identifier,
			token_name_type_pair_order(),
			token_name_type_pair_equality() );

	return (it != identifierMap.end())
		? it->second
		: token_type::tok_identifier;
}

// Gets the type of the given special 2-character range.
token_type::t special_2_token_type(const util::const_char_range &special)
{
	static const token_name_type_vector specialMap = create_special_2_type_map();

	auto it = lean::find_sorted(
			specialMap.begin(), specialMap.end(), special,
			token_name_type_pair_order(),
			token_name_type_pair_equality() );

	return (it != specialMap.end())
		? it->second
		: token_type::unknown;
}

// Gets the type of the given special 1-character range.
token_type::t special_1_token_type(const util::const_char_range &special)
{
	static const token_name_type_vector specialMap = create_special_1_type_map();

	auto it = lean::find_sorted(
			specialMap.begin(), specialMap.end(), special,
			token_name_type_pair_order(),
			token_name_type_pair_equality() );

	return (it != specialMap.end())
		? it->second
		: token_type::unknown;
}

// Gets the name of the given token type.
const char* token_type_name(token_type::t type)
{
	using namespace token_type;

	switch (type)
	{
	case kw_fun: return "fun";
	case kw_def: return "def";
	case kw_var: return "var";
	case kw_struct: return "struct";

	case kw_module: return "module";
	case kw_import: return "import";

	case kw_namespace: return "namespace";
	case kw_using: return "using";
	case kw_typedef: return "typedef";

	case kw_class: return "class";
	case kw_typegroup: return "typegroup";

	case kw_if: return "if";
	case kw_else: return "else";
	case kw_for: return "for";
	case kw_while: return "while";
	case kw_do: return "do";

	case kw_return: return "return";

	case kw_in: return "in";

	case kw_new: return "new";
	case kw_delete: return "delete";

	case kw_throw: return "throw";
	case kw_try: return "try";
	case kw_catch: return "catch";
	case kw_guard: return "guard";
	case kw_unguard: return "unguard";

	case tok_identifier: return "identifier";
	case tok_num_literal: return "numeric literal";
	case tok_char_literal: return "character literal";
	case tok_string_literal: return "string literal";

	case tok_oparen: return "opening parentheses";
	case tok_cparen: return "closing parentheses";
	case tok_obrace: return "opening braces";
	case tok_cbrace: return "closing braces";
	case tok_obracket: return "opening brackets";
	case tok_cbracket: return "closing brackets";

	case tok_semicolon: return "delimiter";
	case tok_comma: return "comma";

	case tok_questionmark: return "question mark";
	
	case tok_op_colon: return "colon";

	case op_period: return "period";

	case op_tilde: return "tilde";
	
	case op_add: return "add";
	case op_sub: return "subtract";
	case op_mul: return "multiply";
	case op_div: return "divide";
	case op_mod: return "modulus";
	
	case op_and: return "and";
	case op_or: return "or";
	case op_xor: return "xor";
	case op_not: return "not";

	case op_to: return "to";
	case op_on: return "on";
	
	case op_increment: return "increment";
	case op_decrement: return "decrement";
	
	case op_logical_and: return "logical and";
	case op_logical_or: return "logical or";
	
	case op_assign: return "assign";

	case op_add_assign: return "add and assign";
	case op_sub_assign: return "subtract and assign";
	case op_mul_assign: return "multiply and assign";
	case op_div_assign: return "divide and assign";
	case op_mod_assign: return "modulus and assign";
	
	case op_and_assign: return "and and assign";
	case op_or_assign: return "or and assign";
	case op_xor_assign: return "xor and assign";
	
	case op_equal: return "equal";
	case op_not_equal: return "not equal";
	case op_less: return "less";
	case op_less_equal: return "less equal";
	case op_greater: return "greater";
	case op_greater_equal: return "greater equal";

	case unknown: return "unknown";
	case end: return "end";

	default:
		LEAN_ASSERT_DEBUG(false);
	}

	return "assertion failed";
}

} // namespace

} // namespace